package com.hmkj.quartz.job;

import com.hmkj.common.utils.DateUtils;
import com.hmkj.common.utils.PriceHelper;
import com.hmkj.core.api.juhe.sdk.JPushClientUtil;
import com.hmkj.core.constant.AliyunCodeKey;
import com.hmkj.core.constant.CacheID;
import com.hmkj.core.constant.ConfigNID;
import com.hmkj.core.constant.PhoneCodeKey;
import com.hmkj.core.eolinker.endpoints.QuotationEndpoint;
import com.hmkj.core.eolinker.models.ModelRealTime;
import com.hmkj.core.model.order.OrderModel;
import com.hmkj.core.model.simulation.SimulationRank;
import com.hmkj.core.po.account.Account;
import com.hmkj.core.po.dailyReport.DailyReport;
import com.hmkj.core.po.holiday.Holiday;
import com.hmkj.core.po.investAccountLog.InvestAccountLog;
import com.hmkj.core.po.order.Order;
import com.hmkj.core.po.simulation.OrderSimulation;
import com.hmkj.core.po.system.MessageTemplate;
import com.hmkj.core.po.system.NoticeLog;
import com.hmkj.core.po.user.User;
import com.hmkj.core.service.account.AccountService;
import com.hmkj.core.service.cache.RedisService;
import com.hmkj.core.service.dailyReport.DailyReportService;
import com.hmkj.core.service.holiday.HolidayService;
import com.hmkj.core.service.investAccountLog.InvestAccountLogService;
import com.hmkj.core.service.order.OrderOperationService;
import com.hmkj.core.service.order.OrderService;
import com.hmkj.core.service.simulation.OrderSimulationService;
import com.hmkj.core.service.sms.SendMsgService;
import com.hmkj.core.service.system.NoticeLogService;
import com.hmkj.core.service.user.UserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.log4j.Logger;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;

/**
 * 订单定时器
 */
@Slf4j
@Component
public class OrderJob {
    @Resource
    private OrderService orderService;
    @Resource
    private AccountService accountService;
    @Resource
    private RedisService redisService;
    @Resource
    UserService userService;
    @Resource
    OrderOperationService orderOperationService;
    @Resource
    DailyReportService dailyReportService;
    @Resource
    InvestAccountLogService investAccountLogService;
    @Resource
    NoticeLogService noticeLogService;
    @Resource
    HolidayService holidayService;
    @Resource
    SendMsgService sendMsgService;

    private final Logger L = Logger.getLogger(OrderJob.class);

    /**
     * 这是一个定时器
     * <p>
     * 1 Seconds (0-59)
     * 2 Minutes (0-59)
     * 3 Hours (0-23)
     * 4 Day of month (1-31)
     * 5 Month (1-12 or JAN-DEC)
     * 6 Day of week (1-7 or SUN-SAT)
     * 7 Year (1970-2099)
     * 取值：可以是单个值，如6；
     * 也可以是个范围，如9-12；
     * 也可以是个列表，如9,11,13
     * 也可以是任意取值，使用*
     * 0 * * * * * 代表每分钟执行一次
     * 2011-09-07 09:23:00
     * 2011-09-07 09:24:00
     * 2011-09-07 09:25:00
     * 2011-09-07 09:26:00
     * <p/>
     * (cron = "0/2 * 9,10,11,13,14,15 * * MON-SAT")
     * 查询到达止损线的订单 ,到达止损线，将订单状态改为平仓
     */
    //
    @Async
    @Scheduled(cron = "0/1 30-59 9 * * MON-FRI")
    @Scheduled(cron = "0/1 * 10 * * MON-FRI")
    @Scheduled(cron = "0/1 0-30 11 * * MON-FRI")
    @Scheduled(cron = "0/1 * 13 * * MON-FRI")
    @Scheduled(cron = "0/1 0-55 14 * * MON-FRI")
    public void arriveStopLossLimit() throws Exception {
        log.info("------------------------止损平仓-------------------------");
        //判断是否是节假日
        if (isHoliday(new Date())) {
            //节假日直接结束
            return;
        }
        List<Order> list = orderService.select(new Order(t -> {
            t.setStatus(Order.STATUS.T3.code);
        }));
        if (CollectionUtils.isNotEmpty(list)) {
            for (Order order : list) {
                //买入成交的数量乘以价格
//                BigDecimal buyMoney = order.getBuyDealPrice().multiply(BigDecimal.valueOf(order.getBuyDealNumber()));
                //从缓存中读取股票信息
                ModelRealTime model = QuotationEndpoint.realtime(order.getEquityCode());
                if (null != model) {
                    if (PriceHelper.isLessThanOrEquals(new BigDecimal(model.getLast_price()), BigDecimal.ZERO)) {
                        continue;
                    }
//                    if (PriceHelper.isLessThan(new BigDecimal(model.getLast_price()), new BigDecimal(model.getPrev_price()).multiply(BigDecimal.valueOf(0.5)))) {
//                        continue;
//                    }
                    //计算当前订单总额
//                    BigDecimal nowMoney = BigDecimal.valueOf(order.getBuyDealNumber()).multiply(new BigDecimal(model.getLast_price()));
                    // 判断是否为当天订单，通过创建时间与当前时间比对。
                    Date date = new Date();
                    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
                    String today = dateFormat.format(date);

                    String buyDay = dateFormat.format(order.getBuyDealTime());
                    if (today.equals(buyDay)) {
                        log.info("当天购买订单");
                        continue;
                    }
                    if(new BigDecimal(model.getLast_price()).compareTo(order.getLoseStopPrice()) != 1){
                        Map<String, Object> data = new HashMap<>();
                        data.put("orderId", order.getId());
                        redisService.push(CacheID.STOP_LOSS, data);
                        //发送通知
                        noticeLogService.insert(new NoticeLog(t -> {
                            t.setType(NoticeLog.TYPE.LETTER.code);
                            t.setUserId(order.getUserId());
                            t.setStatus(NoticeLog.STATUS.SUCCESS.code);
                            t.setTitle("止损平仓提醒");
                            t.setContent("尊敬的用户，您好！您的订单订单号为"+order.getOrderNo()+"触发止损平仓");
                            t.setReadFlag(NoticeLog.READ_FLAG.NOT.code);
                        }));
                    }
//                    if (PriceHelper.isLessThan(nowMoney.subtract(buyMoney), order.getStopLossLine())) {
//                        //如果是当天购买的，则不平仓，T+1
//                        //创建队列，把符合条件的订单放入队列，在队列中处理带平仓订单
//                        Map<String, Object> data = new HashMap<>();
//                        data.put("orderId", order.getId());
//                        redisService.push(CacheID.STOP_LOSS, data);
//                    }
                }
            }
        }
    }

    /*
        提示递延费不足的定时器，早上九点，中午，晚上  短信
     */
    @Async
    @Scheduled(cron = "0 0 9,12,18  * * MON-FRI")
    public void warning() throws Exception {
        log.info("---------------------------递延费不足定时器---------------------------");
        //判断是否是节假日
        if (isHoliday(new Date())) {
            //节假日直接结束
            return;
        }
        //根据userId，查询某用户所有的订单需要的递延费
        List<OrderModel> orderList = orderService.selectDiYanGroupByUser();
        try {
            if (CollectionUtils.isNotEmpty(orderList)) {
                for (OrderModel orderModel : orderList) {
                    //根据userId和点买总额，查询用户是否有足够的余额去支付递延费
                    //  递延费
                    BigDecimal diyanFee = redisService.get(ConfigNID.DIYAN_FEE, BigDecimal.class);
                    //订单所需要的递延费
                    BigDecimal totalDiyan = diyanFee.multiply(orderModel.getSumTradeCredit());
                    Account account = accountService.selectByUID(orderModel.getUserId());
                    if (PriceHelper.isLessThan(account.getAvailable(), totalDiyan.add(orderModel.getSumUnpaidDiYan()))) {
                        User user = userService.selectByPrimaryKey(orderModel.getUserId());
                       //发送通知
                        noticeLogService.insert(new NoticeLog(t -> {
                            t.setType(NoticeLog.TYPE.LETTER.code);
                            t.setUserId(user.getId());
                            t.setStatus(NoticeLog.STATUS.SUCCESS.code);
                            t.setTitle("递延费不足提醒");
                            t.setContent("尊敬的用户，您好！您的账户余额不足以支付递延费，请及时充值");
                            t.setReadFlag(NoticeLog.READ_FLAG.NOT.code);
                        }));
                        //发送短信
                        sendSmsCode(AliyunCodeKey.MSG_DIYAN,"递延费", user.getId(),user.getMobilePhone());
                        //推送
                        JPushClientUtil.sendToAliasId(String.valueOf(orderModel.getUserId()), "尊敬的用户，您好！您的账户余额不足以支付递延费，请及时充值！", "", "", "");
                    }
                }
            } else {
                log.info("空");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    //匹配中的订单，过60s流单
    @Async
    @Scheduled(cron = "0/1 * 9,10,11,13,14,15  * * MON-FRI")
    public void matchTimeout() {
        log.info("---------------------------60s流单---------------------------");
        //判断是否是节假日
        if (isHoliday(new Date())) {
            //节假日直接结束
            return;
        }

        String flowTime = redisService.get(ConfigNID.ORDER_EXSIT_TIME);
        List<Order> list = orderService.select(new Order(t -> {
            t.setStatus(Order.STATUS.T1.code);
        }));
        if (CollectionUtils.isNotEmpty(list)) {
            for (Order order : list) {
                Date date = new Date();
                if (date.getTime() >= order.getBuyTime().getTime() + Integer.parseInt(flowTime) * 1000) {
                    Map<String, Object> data = new HashMap<>();
                    data.put("orderId", order.getId());
                    redisService.push(CacheID.QUEUE_FLOW, data);
                }
            }
        }
    }

    /**
     * 处理需要递延的订单，并收取手续费
     * 处理时间 每天休市前30分钟执行
     *
     * @throws Exception ex
     */
    @Async
    @Scheduled(cron = "0 55 14 * * MON-FRI")
    public void handleDefer() throws Exception {
        log.info("----------------处理需要递延的订单-------------------");
        //判断是否是节假日
        if (isHoliday(new Date())) {
            //节假日直接结束
            return;
        }
        /*
            1. 按照用户分组查询出当前有正在持仓订单的用户 -(根据用户查询其所有点买时间不是当天的持仓订单。)
            2.1.计算当前订单的盈亏金额 如果盈亏金额为负数且小于递延阈值(递延阈值=履约保证金 - (递延比例 * 点买金额)) 则平仓。
               2.1.1 平仓时需要判断该订单是否是持仓中的状态。
            3.1 如果盈亏金额为正数则计算递延费并收取递延费
                3.1 如果该用户拥有一笔待平仓状态的订单，则不判断递延费是否充足。
                3.2 如果该用户没有任意一笔待平仓状态的订单。则判断递延费是否充足
                    3.2.1 用户余额充足 直接扣除 持仓过夜。
                    3.2.2 用户余额不充足   则查询该用户所有持仓中点买金额最小盈利最大的单子并平仓掉
        */

        //分组查询出当前有正在持仓订单的用户
        //查询持仓中的订单
        List<Order> holdingList = orderService.select(new Order(t -> {
            t.setStatus(Order.STATUS.T3.code);
        }));

        //创建线程池
        ExecutorService executorService = Executors.newCachedThreadPool();

        //持仓中的订单用户分组
        if (CollectionUtils.isNotEmpty(holdingList)) {
            // 订单按照用户分组
            Map<Integer, List<Order>> holdingMap = orderService.splitGroupByUser(holdingList);
            // 组循环校验用户隔夜
            for (Integer key : holdingMap.keySet()) {
                List<Order> holding = holdingMap.get(key);
                holding = holding.stream().sorted(Comparator.comparing(Order::getTradeCredit).reversed()).collect(Collectors.toList());
                User user = userService.selectByPrimaryKey(key);
                if (user == null) {
                    log.error("处理需要递延的订单失败，用户不存在，用户ID：{}", key);
                    continue;
                }
                //该用户所有待递延的订单
                List<Order> waitDeferOrders = new ArrayList<>();
                //触发递延条件的待平仓订单
                List<Order> waitClosePositionOrders = new ArrayList<>();
                for (Order o : holding) {
                    //判断该订单是否是当天的，如果是则不计算递延费
                    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
                    String today = dateFormat.format(new Date());
                    String orderBuyTime = dateFormat.format(o.getBuyTime());
                    if (today.equalsIgnoreCase(orderBuyTime)) {
                        continue;
                    }
                    //计算当前订单的盈亏金额
                    BigDecimal deferMoney = o.getLossLimit();
                    //计算当前盈亏(盈亏 = 当前市值 - 买入市值)
                    BigDecimal currentProfit = orderService.getOrderCurrentProfit(o.getId());
                    //判断当前盈亏是否小于等于递延线(小于递延线则直接改状态为平仓中)
                    if (!PriceHelper.isEquals(currentProfit, new BigDecimal(1.111))) {
                        if (currentProfit.doubleValue() <= deferMoney.doubleValue()) {
                            String operation = "订单ID: 【 " + o.getId() + " 】 于 " + DateUtils.dateStr4(new Date()) + " 因【触发递延条件】 被系统强制平仓";
                            orderOperationService.saveOrderOperation(o, operation);
                            //利用线程池执行更新方法  不影响定时器的执行时间
                            executorService.execute(() -> orderService.updateByPrimaryKeySelective(new Order(t -> {
                                t.setId(o.getId());
                                t.setStatus(Order.STATUS.T5.code);
                                t.setPingcangType(Order.PINGCANGTYPE.T5.code);
                                t.setEveningUpTime(DateUtils.getNow());
                            })));
                            waitClosePositionOrders.add(o);
                        } else {
                            //该订单需要递延  放入待递延集合
                            waitDeferOrders.add(o);
                        }
                    } else {
                        //停牌直接递延
                        waitDeferOrders.add(o);
                    }
                }

                //循环遍历待递延订单 计算总递延费
                BigDecimal totalDeferMoney = orderService.getTotalDeferMoney(waitDeferOrders);
                //判断余额是否充足
                Account account = accountService.selectByUID(user.getId());
                if (account.getAvailable().compareTo(totalDeferMoney) == -1) {
                    //没有待平仓的订单 则判断账户余额是否有充足的递延费 没有则查找持仓中点买金额最小 收益最大的订单并平仓它
                    List<Order> pcOrders = orderService.select(new Order(t -> {
                        t.setUserId(user.getId());
                        t.setStatus(Order.STATUS.T5.code);
                    }));
                    if (CollectionUtils.isEmpty(waitClosePositionOrders) && CollectionUtils.isEmpty(pcOrders)) {
                        Order minTradeCreditOrMaxProfitOrder = orderService.getMinTradeCreditOrMaxProfitOrder(holding);
                        String operation = "订单ID: 【 " + minTradeCreditOrMaxProfitOrder.getId() + " 】 于 " + DateUtils.dateStr4(new Date()) + " 因【递延费不足】 被系统强制平仓";
                        orderOperationService.saveOrderOperation(minTradeCreditOrMaxProfitOrder, operation);
                        //利用线程池执行更新方法  不影响定时器的执行时间
                        executorService.execute(() -> orderService.updateByPrimaryKeySelective(new Order(t -> {
                            t.setId(minTradeCreditOrMaxProfitOrder.getId());
                            t.setProfitCount(BigDecimal.ZERO);
                            t.setStatus(Order.STATUS.T5.code);
                            t.setPingcangType(Order.PINGCANGTYPE.T3.code);
                            t.setEveningUpTime(DateUtils.getNow());
                        })));
                        //从waitDeferOrders去掉minTradeCreditOrMaxProfitOrder
                        waitDeferOrders.remove(minTradeCreditOrMaxProfitOrder);
                    }
                }
                if (waitDeferOrders.size() > 0) {
                    //保存该用户待递延的订单到redis中
                    redisService.put(CacheID.PREFIX_USER_WAIT_DEFER_ORDERS + user.getId(), waitDeferOrders);
                }
            }
        }
    }

    /*
        15.01分的时候，将平仓不掉的改为持仓中
     */
    @Async
    @Scheduled(cron = "0 1 15 * * MON-FRI")
    public void revokeToHolding() {
        log.info("----------------将平仓不掉的改为持仓中-------------------");
        //判断是否是节假日
        if (isHoliday(new Date())) {
            //节假日直接结束
            return;
        }
        //用户平仓中的订单
        List<Order> list = orderService.select(new Order(t -> {
            t.setStatus(Order.STATUS.T5.code);
        }));
        if (CollectionUtils.isNotEmpty(list)) {
            for (Order order : list) {
                List<Order> waitDefer = new ArrayList<>();
                //保存该用户待递延的订单到redis中
                String operation = "订单ID: 【 " + order.getId() + " 】 于 " + DateUtils.dateStr4(new Date()) + " 因【平仓不成功】 重新变成【持仓中】";
                orderOperationService.saveOrderOperation(order, operation);
                orderService.updateByPrimaryKeySelective(new Order(t -> {
                    t.setId(order.getId());
                    t.setStatus(Order.STATUS.T3.code);
                }));
                List<Order> waitDeferOrders = redisService.getList(CacheID.PREFIX_USER_WAIT_DEFER_ORDERS + order.getUserId(), Order.class);
                if (CollectionUtils.isNotEmpty(waitDeferOrders)) {
                    waitDeferOrders.add(order);
                    redisService.put(CacheID.PREFIX_USER_WAIT_DEFER_ORDERS + order.getUserId(), waitDeferOrders);
                } else {
                    waitDefer.add(order);
                    redisService.put(CacheID.PREFIX_USER_WAIT_DEFER_ORDERS + order.getUserId(), waitDefer);

                }
            }
        }
    }

    /**
     * 处理待递延的订单
     *
     * @throws Exception ex
     */
    @Async
    @Scheduled(cron = "0 5 15 * * MON-FRI")
    public void handlerWaitDeferOrders() throws Exception {
        log.info("----------------处理待递延的订单-------------------");
        //判断是否是节假日
        if (isHoliday(new Date())) {
            //节假日直接结束
            return;
        }
        //从redis中获取待递延的订单
        Set<String> keys = redisService.getKeys(CacheID.PREFIX_USER_WAIT_DEFER_ORDERS + "*");
        for (String key : keys) {
            List<Order> orderList = redisService.getList(key, Order.class);
            if (CollectionUtils.isNotEmpty(orderList)) {
                for (Order order : orderList) {
                    try {
                        orderService.handlerWaitDeferOrders(order.getId());
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
                redisService.del(key);
            }
        }
    }

    /**
     * 每天生成当天的日报表(盈利，综合管理费，递延费，佣金，印花税)
     */
    @Async
    @Scheduled(cron = "0 0 16 * * MON-FRI")
    public void generateDailyReport() {
        log.info("----------------每天生成当天的日报表（盈利，综合管理费，递延费，佣金，印花税）-------------------");
        //判断是否是节假日
        if (isHoliday(new Date())) {
            //节假日直接结束
            return;
        }
        //综合管理费
        List<Order> list = orderService.selectTodayDealOrder();
        BigDecimal manageFee = BigDecimal.ZERO;
        if (CollectionUtils.isNotEmpty(list)) {
            for (Order order : list) {
                manageFee = manageFee.add(order.getPaidFee());
            }
        }
        //印花税，佣金
        BigDecimal yinhuaFee = BigDecimal.ZERO;
        BigDecimal stockFee = BigDecimal.ZERO;
        List<InvestAccountLog> feeList = investAccountLogService.selectTodayInvestAccountLog();
        if (CollectionUtils.isNotEmpty(feeList)) {
            for (InvestAccountLog log : feeList) {
                if (log.getTaxYinhua() != null) {
                    yinhuaFee = yinhuaFee.add(log.getTaxYinhua());
                }
                if (log.getFeeTrade() != null) {
                    stockFee = stockFee.add(log.getFeeTrade());
                }
            }
        }
        //平台 盈利
        BigDecimal profit = BigDecimal.ZERO;
        List<Order> settleList = orderService.selectTodaySettleOrder();
        if (CollectionUtils.isNotEmpty(settleList)) {
            for (Order order : settleList) {
                if (PriceHelper.isGreaterThan(order.getProfitCount(), BigDecimal.ZERO)) {
                    //用户盈利，平台赚取盈利的10%
                    profit = profit.add(order.getProfitCount().multiply(BigDecimal.valueOf(0.1)));
                } else if (PriceHelper.isLessThan(order.getProfitCount(), BigDecimal.ZERO) && PriceHelper.isGreaterThan(order.getProfitCount().abs(), order.getDeposit())) {
                    //穿仓，平台亏损 亏损金额-履约保证金
                    BigDecimal loss = order.getProfitCount().abs().subtract(order.getDeposit());
                    profit = profit.subtract(loss);
                } else if (PriceHelper.isLessThan(order.getProfitCount(), BigDecimal.ZERO)) {
                    //平台保本
                }
            }
        }
        //递延费
        BigDecimal totalFee = redisService.get(CacheID.DEFER_FEE_COUNT, BigDecimal.class);

        BigDecimal finalManageFee = manageFee;
        BigDecimal finalProfit = profit;
        BigDecimal finalStockFee = stockFee;
        BigDecimal finalYinHuaFee = yinhuaFee;
        dailyReportService.insert(new DailyReport(t -> {
            t.setDefer(totalFee == null ? BigDecimal.ZERO : totalFee);
            t.setComprehensive(finalManageFee);
            t.setProfit(finalProfit);
            t.setCommission(finalStockFee);
            t.setStampTax(finalYinHuaFee);
        }));

        redisService.del(CacheID.DEFER_FEE_COUNT);
    }

    /**
     * 模拟账户月排名
     */
//    @Async
//    @Scheduled(cron = "0 0 9,12,15,18 * * MON-FRI")
//    public void simulationRank() {
//        Date date = new Date();
//        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMM");
//        String today = dateFormat.format(date);
//        today = today + "01000000";
//
//        Calendar calendar = Calendar.getInstance();
//        calendar.set(Calendar.DAY_OF_YEAR, calendar.get(Calendar.DAY_OF_YEAR) - 29);
//        Date time = calendar.getTime();
//        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
//        String today = dateFormat.format(time);
//
//        //月排名
//        List<OrderSimulation> list = orderSimulationService.selectSimulationRank(today);
//        List<SimulationRank> simulationRanks = new ArrayList<>();
//        if (CollectionUtils.isNotEmpty(list)) {
//            for (OrderSimulation o : list) {
//                SimulationRank rank = new SimulationRank();
//                rank.setUserId(o.getUserId());
//                rank.setProfitCount(o.getProfitCount());
//                simulationRanks.add(rank);
//            }
//        }
//        redisService.put(CacheID.SIMULATION_RANK_PREFIX, simulationRanks);
//    }


    public boolean isHoliday(Date date) {
        //true表示 是节假日，false表示不是
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMdd");
        String today = simpleDateFormat.format(date);
        List<Holiday> holidays = holidayService.selectAll();
        if (holidays != null && holidays.size() > 0) {
            for (Holiday holiday : holidays) {
                if (today.equals(holiday.getHoliday())) {
                    return true;
                }
            }
        } else {
            return false;
        }
        return false;
    }

    /**
     * 发送通知短信
     *
     * @param codeKey 短信模板对应枚举
     * @param mobile  手机号
     * @param userId  userId
     * @throws Exception
     */
    public void sendSmsCode(AliyunCodeKey codeKey, String title, Integer userId,String mobile) throws Exception {
        sendMsgService.pushMessageQueueForAli(codeKey.template,title,  new HashMap<>(), userId, mobile);
    }
}